package demo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

@SpringBootApplication
@Slf4j
public class Question5Application implements ApplicationRunner {

    /**
     * 定义开始时间戳：2022-01-01 00:00:00
     */
    private static final long BEGIN_TIMESTAMP = 1640995200L;
    /**
     * 序列号的位数
     */
    private static final int COUNT_BITS = 32;
    public static final String CLICK_NUM_KEY = "click_num";
    final String studentKey = "for_student";
    final String bitKey = "bit_key";
    @Autowired
    @Lazy
    private RedisTemplate<String, Student> redisTemplate;

    @Autowired
    private RedisTemplate redisTemplate1;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public static void main(String[] args) {
        SpringApplication.run(Question5Application.class, args);
    }

    //	写入学生
    private void WriteStudents() {
        List<Student> students = Arrays.asList(new Student("zhang", 88), new Student("wang", 87), new Student("li", 99), new Student("zhao", 93), new Student("xi", 96));
        for (Student student : students) {

            redisTemplate.opsForZSet().add(studentKey, student, student.getScore());
        }
    }

    //生成全局唯一ID
//    参考：https://blog.csdn.net/qq_45813393/article/details/127767701
    public long nextId(String keyPrefix) {
        //1.生成时间戳
        LocalDateTime now = LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timeStamp = nowSecond - BEGIN_TIMESTAMP;
        //2.生成序列号
        //2.1 获取当前日期，精确到天
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        //2.2 自增长/计数器  该key的设计不仅可以解决redis自增数上限的问题，还便于统计某一天/月/年的订单的数量
        long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
        //3.拼接并返回
        return timeStamp << COUNT_BITS | count;
    }

    public boolean getbit(String key, Long offset) {
        return redisTemplate.opsForValue().getBit(key, offset);
    }

    public void addbit(String key, Long offset) {
        redisTemplate.opsForValue().setBit(key, offset, true);
    }

    @Bean
    public RedisTemplate<String, Student> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Student> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }


    @Override
    public void run(ApplicationArguments args) {
//        实现分数排名或者排行榜；
        rank();
//        实现全局 ID 生成；Redis自增的方式实现
        for (int i = 0; i < 10; i++) {
            System.out.println("唯一id:" + nextId("unique_id"));
        }

//        -   基于 Bitmap 实现 id 去重；
        Long id = 1001L;
//        addbit(bitKey,id);
        boolean getbit = getbit(bitKey, id);
        System.out.println("getbit:"+getbit);

//        -   基于 HLL 实现点击量计数13

        hyperLogLog();
    }

    private void hyperLogLog() {
        redisTemplate1.opsForHyperLogLog().add(CLICK_NUM_KEY,"user1","user2","user3");
        redisTemplate1.opsForHyperLogLog().add(CLICK_NUM_KEY,"user1","user2","user4");
        Long size = redisTemplate1.opsForHyperLogLog().size(CLICK_NUM_KEY);
        System.out.println("size is:"+size);

    }

    private void rank() {
        System.out.println("test");
        log.info("for test:{}", "some info");
        WriteStudents();
        Set<Student> range = redisTemplate.opsForZSet().reverseRangeByScore(studentKey, 0, 100);
        for (Student student : range) {
            System.out.println(student);
        }
    }


}
